class IssuesController < ApplicationController

  # eager loading settings for get_issue, by action
  PRELOAD = {
      :index => [ :status, :priority, :submitter ],
      :show => [ :status, :attachments,
                 { :comments => [ :user, :attachments, :issue_changes ] },
                 :submitter,
                 :owners
               ],
      :update => [ :subscribers, :status, :priority, :attachments ],
      :edit => [ :status, :priority, :attachments ]
    }

  before_filter :common
  # in the :only block, list the actions that need to have an @issue
  before_filter :get_issue, :only => [ :show, :edit, :update, :update_actions, :assign, :delete ]
  before_filter :authorize, :only => [ :new, :create, :edit, :update, :update_actions, :assign, :destroy ]

  def all_issues
    @issues = Issue.find( :all )

    respond_to do |format|
      format.html # all_issues.html.erb
      format.xml  { render :xml => @issues }
      format.json { render :json => @issues.to_json }
    end
  end

  # Handle the default listing of all open issues
  #
  # Sample URLs that this function handles:
  # GET /projects/foo/issues
  # GET /projects/foo/issues.xml
  # GET /projects/foo/issues.json
  def index
    @issues = Issue.find_open_for(@project)
    @has_feed = true

    respond_to do |format|
      format.html # index.html.erb
      format.rss  # index.rss.builder
      format.xml  { render :xml => @issues }
      format.json { render :json => @issues.to_json }
    end
  end

  # Handle getting all closed issues for a project
  def closed
    @issues = Issue.find_closed_for(@project)
    @has_feed = true
    
    respond_to do |format|
      format.html { render :action => :index }
      format.rss  # closed.rss.builder
      format.xml  { render :xml => @issues }
      format.json { render :json => @issues.to_json }
    end
  end

  # Handle getting all issues for a project
  def all
    @issues = @project.issues
    @has_feed = true
    
    respond_to do |format|
      format.html { render :action => :index }
      format.rss  # all.rss.builder
      format.xml  { render :xml => @issues }
      format.json { render :json => @issues.to_json }
    end
  end

  # Show a specific issue based on an id
  #
  # Sample URLs that this function handles:
  # GET /projects/foo/issues/1
  # GET /projects/foo/issues/1.xml
  # GET /projects/foo/issues/1.json
  def show
    # gets @issue from :get_issue filter
    # thought this would speed things up, but its not causing cache hits:
    # @comment_attachments = @issue.comment_attachments
    @has_feed = true

    respond_to do |format|
      format.html # show.html.erb
      format.rss  # show.rss.builder
      format.xml  { render :xml => @issue }
      format.json { render :json => @issue.to_json }
    end
  end

  # GET /projects/project1/issues/new
  # GET /projects/project1/issues/new.xml
  # GET /projects/project1/issues/new.json
  def new
    @issue = Issue.new

    respond_to do |format|
      format.html # new.html.erb
      format.xml  { render :xml => @issue }
      format.json { render :json => @issue.to_json }
    end
  end

  # GET /issues/1/edit
  def edit
    # gets @issue from :get_issue filter
  end

  # Place a newly created issue into the model
  #
  # Sample urls that will invoke this function
  # POST /projects/project1/issues
  # POST /projects/project1/issues.xml
  # POST /projects/project1/issues.json
  def create
    # fix priority and status (turn the strings into objects)
    if params[:issue]
      if params[:issue][:priority]
        p = Priority.find_or_create_by_name( params[:issue][:priority] )
        params[:issue][:priority] = p
      end
  
      if params[:issue][:status]
        if params[:issue][:status].empty?
          params[:issue][:status] = nil
        else
          s = Status.find_or_create_by_name( params[:issue][:status] )
          params[:issue][:status] = s
        end
      end
    end

    @issue = Issue.new(params[:issue])
    @issue.project = @project
    @issue.submitter = @current_user
    @issue.attachments = get_attachments

    respond_to do |format|
      if @issue.save
        flash[:notice] = 'Issue was successfully created.'
        send_new_email( @project, @issue )
        format.html { redirect_to( issue_url( @project.name, @issue.local_id ) ) }
        format.xml  { render :xml => @issue, :status => :created, :location => @issue }
        format.json { render :json => @issue.to_json, :status => :created, :location => @issue }
      else
        format.html { render :action => "new" }
        format.xml  { render :xml => @issue.errors, :status => :unprocessable_entity }
        format.json { render :json => @issue.to_json, :status => :unprocessable_entity }
      end
    end
  end

  # Update the details of an issue
  #
  # Sample urls that will invoke this function
  # PUT /projects/project1/issues/1
  # PUT /projects/project1/issues/1.xml
  # PUT /projects/project1/issues/1.json
  def update
    # gets @issue from :get_issue filter
    @comment = Comment.new
    @comment.user = @current_user
    @comment.issue_changes = @issue.changes( params[:issue] )
    fields = @comment.issue_changes.collect do |c|
      if c.is_a? IssueRefChange
        c.changed_to_type.to_s
      else
        c.attr.capitalize if c.is_a? IssueAttrChange
      end
    end
    old_attachments = @issue.attachments.to_set
    @issue.attachments = get_attachments
    fields << 'Attachments' if old_attachments.to_set != @issue.attachments.to_set
    @comment.summary = 'Edited issue: ' + fields.compact.join( ', ' )

    respond_to do |format|
      if fields.any?
        @issue.comments << @comment
        if @comment.save and @issue.update_attributes(params[:issue])
          flash[:notice] = 'Issue was successfully updated.'
          # TODO: add a notification here, once editing is working properly
          format.html { redirect_to( issue_url( @project.name, @issue.local_id ) ) }
          format.xml  { head :ok }
  	      format.json { :ok.to_json }
        else
          format.html { render :action => "edit" }
          format.xml  { render :xml => @issue.errors, :status => :unprocessable_entity }
          format.json { render :json => @issue.errors.to_json, :status => :unprocessable_entity }
        end
      else
        format.html { redirect_to( issue_url( @project.name, @issue.local_id ) ) }
        format.xml  { head :ok }
  	    format.json { :ok.to_json }
      end
    end
  end

  # POST /projects/project1/issues/1/update_actions?a=1&v=1&s=1
  def update_actions
    # adoption
    if params[:a]
      update_details = true
      update_adoption = true
      if params[:a].to_i == 1
        @current_user.adopt( @issue )
      elsif params[:a].to_i == 0
        @current_user.disown( @issue ) do |assigner|
          if assigner.notify_on_disown?
            Notifier.deliver_issue_disowned( assigner, @current_user, @issue )
          end
        end
      end
    end

    if params[:s]
      update_subscribe = true
      if params[:s].to_i == 1
        @current_user.subscribe( @issue )
      elsif params[:s].to_i == 0
        @current_user.unsubscribe( @issue )
      end
    end

    if params[:v]
      update_votes = true
      if params[:v].to_i == 1
        Vote.for( @issue, @current_user )
      elsif params[:v].to_i == 0
        Vote.against( @issue, @current_user )
      end
    end

    respond_to do |format|
      format.html { render :partial => 'adopt', :locals => { :user => @current_user, :issue => @issue } }
      format.js do
        render :update do |page|
          page.replace( 'flash',
                        :partial => 'shared/flash',
                        :locals => { :flash => flash } )
          page.replace( 'adoption',
                        :partial => 'adopt',
                        :locals => {
                          :user => @current_user,
                          :project => @project,
                          :issue => @issue
                        } ) if update_adoption
          page.replace( 'details',
                        :partial => 'details',
                        :locals => { :issue => @issue }
                      ) if update_details
          page.replace( 'subscription',
                        :partial => 'subscription',
                        :locals => {
                          :user => @current_user,
                          :project => @project,
                          :issue => @issue
                        } ) if update_subscribe
          page.replace( 'votes',
                        :partial => 'vote',
                        :locals => {
                          :user => @current_user,
                          :project => @project,
                          :issue => @issue
                        } ) if update_votes
                        
        end
      end
    end
  end

  # POST /projects/project1/issues/1/assign?user=blue
  def assign
    errors = []
    assignee = User.find_by_dn( params[:user][:name] ) if params[:user]
    if assignee
      Adoption.assign( @issue, assignee, @current_user )
    else
      errors << "Could not find user: #{params[:user][:name]}"
    end

    unless errors.size > 0
      if assignee.notify_on_assignment?
        Notifier.deliver_issue_assigned( @current_user, assignee, @issue )
      end

      respond_to do |format|
        format.js do
          render :update do |page|
            page.replace( 'details',
                          :partial => 'details',
                          :locals => { :issue => @issue } )
            page.replace( 'adoption',
                          :partial => 'adopt',
                          :locals => {
                            :user => @current_user,
                            :project => @project,
                            :issue => @issue
                          } )
            page.hide( 'assign_form' )
          end
        end
      end
    else
      respond_to do |format|
        format.js do
          render :update do |page|
            page.replace_html( 'assign_error_msg',
                               errors.join( ', ' )
                             )
            page.hide( 'assign_form' )
            page.show( 'assign_errors' )
          end
        end
      end
    end
  end

  # Get rid of an issue
  #
  # Sample urls that will invoke this function
  # DELETE /projects/project1/issues/1
  # DELETE /projects/project1/issues/1.xml
  # DELETE /projects/project1/issues/1.json
  def destroy
    # gets @issue from :get_issue filter

    @issue.destroy

    respond_to do |format|
      format.html { redirect_to(issues_url) }
      format.xml  { head :ok }
      format.json { head :json }
    end
  end

private

  def common
    @project = Project.find_by_name_or_id( params[:project_id] )
  end

  def get_issue
    @issue = Issue.find_project_local( @project, params[:id],
             :include => PRELOAD[params[:action].to_sym] )
  end

end
